home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / news / misc / newsxd-2.5.1-shar / process.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-06-08  |  19.5 KB  |  611 lines

  1. /*
  2.  * #include <legal/bs.h>
  3.  >
  4.  > Copyright (c) 1989 Washington University in Saint Louis, Missouri and
  5.  > Chris Myers. All rights reserved.
  6.  >
  7.  > Permission is hereby granted to copy, reproduce, redistribute or
  8.  > otherwise use this software as long as: (1) there is no monetary
  9.  > profit gained specifically from the use or reproduction of this
  10.  > software, (2) it is not sold, rented, traded, or otherwise marketed,
  11.  > (3) the above copyright notice and this paragraph is included
  12.  > prominently in any copy made, and (4) that the name of the University
  13.  > is not used to endorse or promote products derived from this software
  14.  > without the specific prior written permission of the University.
  15.  > THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  16.  > IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  17.  > WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18.  >
  19.  */
  20.  
  21. #include "defs.h"
  22. extern int errno;
  23.  
  24. /*************************************************************************/
  25. /* FUNCTION  : kill_children                                             */
  26. /* PURPOSE   : Kills all outstanding transmitters.  Kill_children will   */
  27. /*             wait thirty seconds to allow all of the transmitters to   */
  28. /*             exit gracefully.  If invoked by a signal (SIGTERM?), exit */
  29. /*             else return to caller.                                    */
  30. /* ARGUMENTS : Signal number or 0 if to return to caller                 */
  31. /*************************************************************************/
  32.  
  33. void
  34. kill_children(sig)
  35.    int  sig;            /* sig will be nonzero if called by SIGTERM */
  36.  
  37. {
  38. struct  host    *hostptr;
  39.  
  40.    if ((hostlist == NULL) && (sig == 0)) return;
  41.  
  42.    log(LOG_INFO, "newsxd: shutting down all transmitters\n");
  43.  
  44.    foreach (hostptr, hostlist) {
  45.       if (hostptr->pid != 0) {
  46.          dprintf("newsxd: killing transmitter for %s\n", hostptr->hostname);
  47.          (void) kill (hostptr->pid, SIGTERM);
  48.       }
  49.    }
  50.  
  51.    if (sig == 0) return;
  52.  
  53.    dprintf("(kill_children): sleeping 30 seconds\n");
  54.  
  55.    (void) sleep(30);            /* allow transmitters to quit gracefully */
  56.  
  57.    log(LOG_INFO, "shut down by signal %d\n", sig);
  58.  
  59.    if (debug == 0 && DEBUG == 0) {
  60.       (void) unlink(pidfile);
  61.       (void) unlink(statusfile);
  62.    }
  63.  
  64.    (void) exit(0);              /* invoked by SIGTERM, time to quit */
  65.  
  66. }
  67.  
  68. /*************************************************************************/
  69. /* FUNCTION  : xmit_done                                                 */
  70. /* PURPOSE   : Catch the SIGCHLD from completed transmitters and do the  */
  71. /*             necessary cleanup.  Also wait synchronously for a xmitter */
  72. /*             to complete; also perform forced cleanup for an xmitter   */
  73. /* ARGUMENTS : sig, > 0 if xmit_done is called by SIGCHLD                */
  74. /*                 == 0 if called to check for a zombie w/NOHANG         */
  75. /*                 ==-1 if called to sync wait for a transmitter         */
  76. /*                 < -1 to perform a forced cleanup on a transmitter     */
  77. /*************************************************************************/
  78.  
  79. void
  80. xmit_done(sig)
  81.    int  sig;                 /* sig will be nonzero if called by SIGCHLD */
  82.  
  83. {
  84. struct  host    *hostptr;
  85. struct  class   *classptr;
  86. union   wait    status;
  87. struct  rusage  usage;
  88. extern    char    *sys_errlist[];
  89.  
  90. int     options,
  91.     loop,
  92.         pid;
  93.  
  94.    (void) sigsetmask(0);
  95.  
  96.    /*
  97.     * Try and catch any completed transmitters first and process them.
  98.     * Then, if xmit_done() was called to synchronously wait on a transmitter
  99.     * or to perform forced cleanup on a transmitter, do it.
  100.     */
  101.  
  102.    Dprintf("(xmit_done) sig %d\n", sig);
  103.  
  104.    while (1) {
  105.       if (sig != -1) options = WNOHANG;
  106.  
  107.       pid = wait3(&status, options, &usage);
  108.  
  109.       if (pid <= 0) 
  110.          if (sig < -1) {
  111.             pid = -sig;
  112.             sig = 0;
  113.          } else {
  114.             Dprintf("(xmit_done) no process! pid=%d, errno=%s\n", pid,
  115.                sys_errlist[errno]);
  116.             return;
  117.          }
  118.    
  119.       Dprintf("(xmit_done) pid %d\n", pid);
  120.  
  121.       for (loop = 0; loop < MAXXMITTERS; loop++) {
  122.          if (pidlist[loop] == pid) {
  123.             hostptr = pidmap[loop];
  124.             pidlist[loop] = 0;
  125.             pidmap[loop] = (struct host *) NULL;
  126.             hostptr->pid = 0;
  127.             hostptr->whynot = WN_NOTMYTURN;
  128.             foreach (classptr, classlist) {
  129.                if (strcmp(hostptr->class, classptr->classname) == 0) {
  130.                   classptr->curxmits--;
  131.                   freeclassslot(classptr, hostptr->classslot);
  132.                   dprintf("%s: transmission completed\n", hostptr->hostname);
  133.                   return;
  134.                }
  135.             }
  136.  
  137.             /*
  138.              * The (sig < -1) check is here just in case a transmitter exitted
  139.              * normally just as run_queue tries to force a cleanup on it. If
  140.              * this isn't here, newsxd will abort with a corrupted data err.
  141.              */
  142.  
  143.             if (sig < -1) return;
  144.  
  145.             /* Something is seriously wrong here -- quit somewhat gracefully */
  146.  
  147.             logerr("CORRUPTED HOST/CLASS STRUCTURE!\n");
  148.             kill_children(1);
  149.          }
  150.       }
  151.    }
  152. }
  153.  
  154. /*************************************************************************/
  155. /* FUNCTION  : run_queue                                                 */
  156. /* PURPOSE   : Go through the list of hosts to transmit to and start any */
  157. /*             transmitter that meets all of the necessary conditions    */
  158. /* ARGUMENTS : none                                                      */
  159. /*************************************************************************/
  160.  
  161. void
  162. run_queue()
  163.  
  164. {
  165. struct  host    *hostptr;
  166. struct  class   *classptr, *lastclass;
  167. struct  stat    statbuf;
  168. struct  tm      *curtime;
  169.  
  170. int     clock,
  171.         pid,
  172.         hadtheirchance,
  173.     which,
  174.     which2,
  175.         loop,
  176.         loop2,
  177.     hostargc,
  178.     classargc;
  179.  
  180. char    fnbuf[MAXPATHLEN],
  181.         fnbuf2[MAXPATHLEN],
  182.     **hostargv,
  183.     **classargv;
  184.  
  185.    CONFIGCHANGED = 0;
  186.    hadtheirchance = 0;
  187.    lastclass = (struct class *) NULL;
  188.  
  189.    foreach (hostptr, hostlist) {
  190.  
  191.       /*
  192.        * If the configuration has been changed (possibly via SIGHUP), we
  193.        * need to assume that all of the current pointers and such are
  194.        * invalid since the current host MAY have been deleted...
  195.        *
  196.        * There is still a race condition, but this check lessens the chance
  197.        * of problems considerably.
  198.        *
  199.        */
  200.  
  201.       if (CONFIGCHANGED) {
  202.          dprintf("Reconfigured during queue run -- aborting queue run\n");
  203.          CONFIGCHANGED = 0;
  204.          return;
  205.       }
  206.  
  207.       (void) time(&clock);
  208.       curtime = localtime(&clock);
  209.  
  210.       classptr = getclass(hostptr->class);
  211.  
  212.       if (classptr != lastclass) {
  213.          if (lastclass) {
  214.             dprintf("class %s: members %d, hadtheirchance %d\n",
  215.                lastclass->classname, lastclass->members, hadtheirchance);
  216.             if (lastclass->members == hadtheirchance) {
  217.                lastclass->xmitsernum++;
  218.                dprintf("class %s: add 1 to xmitsernum, now %d\n",
  219.                   lastclass->classname, lastclass->xmitsernum);
  220.             }
  221.          }
  222.          hadtheirchance = 0;
  223.          lastclass = classptr;
  224.       }
  225.  
  226.       /*
  227.        * Check and see if we somehow missed the SIGCHLD for a transmitter and
  228.        * it's really gone and we don't know it.  If so, force a cleanup.
  229.        */
  230.  
  231.       if ((hostptr->pid) && (kill(hostptr->pid, 0) == -1) && (errno == ESRCH)) {
  232.          xmit_done(-hostptr->pid);
  233.          hostptr->pid = 0;          /* Just in case xmit_done() missed it! */
  234.       }
  235.  
  236.       /*
  237.        * Check to see if the host has had a transmitter running for more
  238.        * than the ttl of its transmission class.  If so, kill it.
  239.        */
  240.  
  241.       which = (hostptr->options.ttl) ?
  242.          hostptr->options.ttl : classptr->options.ttl;
  243.       which2 = (hostptr->options.ttlpenalty) ?
  244.          hostptr->options.ttlpenalty : classptr->options.ttlpenalty;
  245.  
  246.       dprintf("%s: checking ttl (%d/%d)\n", hostptr->hostname, which, which2);
  247.  
  248.       if ((hostptr->pid > 0) && (clock > (hostptr->lasttime + which))) {
  249.          dprintf("%s: exceeded ttl, killing\n");
  250.          hostptr->penaltytime = clock + which2;
  251.          if (kill(hostptr->pid, SIGTERM) != 0) xmit_done(-hostptr->pid);
  252.          hostptr->whynot = WN_TTL;
  253.          continue;  /* skip this host to give others a chance */
  254.       }
  255.  
  256.       /*
  257.        * If there is already a running transmitter for this host, skip it. We
  258.        * don't want more than one!
  259.        */
  260.  
  261.       dprintf("%s: checking for active daemon (%d)\n", hostptr->hostname,
  262.          hostptr->pid);
  263.  
  264.       if (hostptr->pid > 0) {
  265.          hostptr->whynot = WN_RUNNING;
  266.          hadtheirchance++;
  267.          continue;
  268.       }
  269.  
  270.       /*
  271.        * Check to see if this host has already had a chance to start
  272.        * a transmitter.  If so, let someone else have a chance.
  273.        */
  274.  
  275.       dprintf("%s: xmitsernum is %d, class xmitsernum is %d\n",
  276.          hostptr->hostname, hostptr->xmitsernum, classptr->xmitsernum);
  277.  
  278. /*
  279.       if ((hostptr->xmitsernum == classptr->xmitsernum) &&
  280.           (classptr->maxxmits < classptr->members)) {
  281.  */
  282.  
  283.       if (hostptr->xmitsernum == classptr->xmitsernum) {
  284.          hadtheirchance++;
  285. /*       hostptr->whynot = WN_NOTMYTURN; */
  286.          continue;
  287.       }
  288.  
  289.       /*
  290.        * Check the current time against the last time an xmit was started
  291.        * for this class to make sure we don't start too many daemons at
  292.        * one time.
  293.        */
  294.  
  295.       which = (hostptr->options.startint) ?
  296.          hostptr->options.startint : classptr->options.startint;
  297.  
  298.       dprintf("%s: checking class startup interval (clock is %d, start %d)\n",
  299.          hostptr->hostname, clock, (classptr->laststart + which));
  300.  
  301.       if (clock < (classptr->laststart + which)) {
  302.          hostptr->whynot = WN_CLASSSTARTINT;
  303.          continue;
  304.       }
  305.  
  306.       /*
  307.        * Check the current load and compare against the maximum allowed
  308.        * load for starting new transmitters for this hosts's class.
  309.        */
  310.  
  311.       which = (hostptr->options.maxload) ?
  312.          hostptr->options.maxload : classptr->options.maxload;
  313.  
  314.       dprintf("%s: checking maximum load (cur %d, max %d)\n",
  315.          hostptr->hostname, getla(), which);
  316.  
  317.       if (getla() > which) {
  318.          hostptr->whynot = WN_LOAD;
  319.          continue;
  320.       }
  321.  
  322.       /*
  323.        * Check the number of currently running transmitters for this host's
  324.        * transmission class.  If we're already running at the limit, skip
  325.        * this host.
  326.        */
  327.  
  328.       dprintf("%s (%s): checking running xmit count (%d of %d)\n",
  329.          hostptr->hostname, classptr->classname, classptr->curxmits,
  330.          classptr->maxxmits);
  331.  
  332.       if (classptr->curxmits >= classptr->maxxmits) {
  333.          hostptr->whynot = WN_MAXXMITS;
  334.          continue;
  335.       }
  336.  
  337.       /*
  338.        * All tests after this point should be tests for some characteristic
  339.        * of the host that would cause it to avoid its turn to start its
  340.        * transmitter.  Things like: the host startup interval hasn't passed
  341.        * yet, or it's a bad time to send to that host, or there's no work
  342.        * to send to that host.
  343.        *
  344.        * Tests before this point should be tests for some characteristic not
  345.        * related to the particular host that prevents it from being able to
  346.        * start its transmitter.  Things like: load too high, too many xmits
  347.        * already running...
  348.        */
  349.  
  350.       /*
  351.        * This host had a chance to send news (and can possibly skip it).
  352.        */
  353.  
  354.       hostptr->xmitsernum = classptr->xmitsernum;
  355.       hadtheirchance++;
  356.  
  357.       /*
  358.        * Check to see if the host had a transmitter killed because it ran
  359.        * longer than its class' ttl.  If so, see if its penalty time is
  360.        * still unexpired.
  361.        */
  362.  
  363.       dprintf("%s: checking ttl penalty\n", hostptr->hostname);
  364.  
  365.       if (hostptr->penaltytime > clock) {
  366.          hostptr->whynot = WN_PENALTYTIME;
  367.          continue;
  368.       }
  369.  
  370.       /*
  371.        * Check the current time against the last time an xmit was started
  372.        * for this class to make sure we don't start a transmitter for this
  373.        * host too often...
  374.        */
  375.  
  376.       which = (hostptr->options.interval) ?
  377.          hostptr->options.interval : classptr->options.interval;
  378.  
  379.       dprintf("%s: checking host startup interval (%d)\n", hostptr->hostname,
  380.          which);
  381.  
  382.       if (clock < (hostptr->lasttime + which)) {
  383.          hostptr->whynot = WN_HOSTSTARTINT;
  384.          continue;
  385.       }
  386.  
  387.       /*
  388.        * Check to see that the current time is within the permitted range
  389.        * of transmission times.  If not, skip this host.
  390.        */
  391.  
  392.       dprintf("%s: checking valid transmission times (%s)\n",
  393.          hostptr->hostname, hostptr->times);
  394.  
  395.       if (!validtime(hostptr->times)) {
  396.          hostptr->whynot = WN_BADTIME;
  397.          continue;
  398.       }
  399.  
  400.       /*
  401.        * See if there is an outstanding work file for this host, if so we
  402.        * just need to run a transmitter, otherwise rename the batch file
  403.        * to the work file and run the transmitter.
  404.        */
  405.  
  406.       (void) sprintf(fnbuf, workfile, hostptr->hostname);
  407.  
  408.       dprintf("%s: checking for workfile (%s)\n",
  409.             hostptr->hostname, fnbuf);
  410.  
  411.       if ((loop = stat(fnbuf, &statbuf)) != 0) {
  412.          Dprintf("%s: stat(%s) returned %d (errno is %d)\n", hostptr->hostname,
  413.             fnbuf, loop, errno);
  414.  
  415.          (void) sprintf(fnbuf2, batchfile, hostptr->hostname);
  416.  
  417.          dprintf("%s: checking for batchfile (%s)\n",
  418.                hostptr->hostname, fnbuf2);
  419.  
  420.          if (!classptr->flags[C_NOBATCH]) {
  421.             if ((loop = stat(fnbuf2, &statbuf)) != 0) {
  422.                Dprintf("%s: stat(%s) returned %d (errno is %d)\n",
  423.                   hostptr->hostname, fnbuf, loop, errno);
  424.  
  425.                hostptr->whynot = WN_NOWORK;
  426.                continue;
  427.             } else {
  428.                hostptr->whynot = WN_RENAMEFAILED;
  429.                if (!classptr->flags[C_NOWORK] && rename(fnbuf2, fnbuf) != 0) {
  430.                   dprintf("%s: rename failed (%s to %s)\n",
  431.                         hostptr->hostname, fnbuf2, fnbuf);
  432.                   continue;
  433.                }
  434.             }
  435.          }
  436.       }
  437.       
  438.       /*
  439.        * Fork off a transmitter for this host
  440.        */
  441.  
  442.       hostptr->whynot = WN_RUNNING;
  443.  
  444.       if ((hostptr->classslot = getclassslot(classptr)) == -1) {
  445.          hostptr->whynot = WN_NOSLOT;
  446.          exit(1);
  447.       }
  448.  
  449.       if ((pid = fork()) == 0) {
  450.      /* Child. */
  451.          int fd;
  452.  
  453.          /* Untrap all of the signals for newsxd; this is now a transmitter */
  454.  
  455.          (void) signal(SIGCHLD, SIG_DFL);
  456.          (void) signal(SIGHUP, SIG_DFL);
  457.          (void) signal(SIGQUIT, SIG_DFL);
  458.          (void) signal(SIGTERM, SIG_DFL);
  459.          (void) signal(SIGUSR1, SIG_DFL);
  460.          (void) signal(SIGUSR2, SIG_DFL);
  461.          (void) signal(SIGIO, SIG_DFL);
  462.          (void) signal(SIGIOT, SIG_DFL);
  463.          (void) signal(SIGTRAP, SIG_DFL);
  464.  
  465.          fd = open(classptr->flags[C_NOWORK] ? fnbuf2 : fnbuf, O_RDONLY);
  466.          if (fd > 0) (void) flock(fd, LOCK_EX);
  467.  
  468. #ifdef    CNEWSLOCKING
  469.          if (!classptr->flags[C_NOWORK]) {
  470.  
  471.             /* If we can get a valid lock then we know that since the batch
  472.              * file has been renamed to WORKFILE, cnews will now be writing
  473.              * to a different file (BATCHFILE).
  474.              */
  475.             (void) newslock();
  476.             (void) newsunlock();
  477.          }
  478. #endif    CNEWSLOCKING
  479.  
  480.          Dprintf("(%s): classslot is %d\n", hostptr->hostname,
  481.             hostptr->classslot);
  482.  
  483.          dprintf("%s: spawning transmitter\n", hostptr->hostname);
  484.  
  485.          if (!newsxdebug) {
  486.             (void) sprintf(fnbuf2, xmitlogs, hostptr->hostname);
  487.             (void) freopen(fnbuf2, "a", stdout);
  488.             (void) freopen(fnbuf2, "a", stderr);
  489.          }
  490.  
  491.          (void) fprintf(stderr, "%s: begin at %02d:%02d:%02d\n",
  492.             hostptr->hostname, curtime->tm_hour, curtime->tm_min,
  493.             curtime->tm_sec);
  494.          (void) fflush(stderr);
  495.  
  496.          which = (hostptr->options.deltanice) ?
  497.             hostptr->options.deltanice : classptr->options.deltanice;
  498.  
  499.          if (which != 0) {
  500.             Dprintf("Changing transmitter nice by %d for %s\n",
  501.                which, hostptr->hostname);
  502.  
  503.             (void) nice(which);
  504.          }
  505.  
  506.          if (classptr->xargc == 0) {
  507.             classptr = getclass("DEFAULT");
  508.             if (classptr->xargc == 0) {
  509.                logerr("host %s: No DEFAULT xmitter defined -- aborting\n",
  510.                   hostptr->hostname);
  511.                (void) _exit(1);
  512.             }
  513.          }
  514.  
  515.          Dprintf("classptr->xargc = %d\n", classptr->xargc);
  516.  
  517.          Dprintf("%s: EXECing %s with parameters:\n",
  518.             hostptr->hostname, classptr->xpath);
  519.  
  520.          for (loop = 0; loop <= classptr->xargc; loop++) {
  521.             if (classptr->xargv[loop] == NULL) {
  522.                Dprintf("arg[%d] = NULL\n", loop);
  523.             } else {
  524.                Dprintf("arg[%d] = %s\n", loop, classptr->xargv[loop]);
  525.             }
  526.          }
  527.  
  528.          classargc = classptr->xargc;
  529.          classargv = classptr->xargv;
  530.  
  531.          classargv[classargc] = NULL;
  532.  
  533.          for (loop = 0; loop < classargc; loop++) {
  534.             hostargv = NULL;
  535.             if (strcmp(classargv[loop], "%f") == NULL) {
  536.                hostargc = hostptr->xargc;
  537.                hostargv = hostptr->xargv;
  538.             }
  539.  
  540.             if (strcmp(classargv[loop], "%d") == NULL) {
  541.                struct class *myclassptr = getclass(hostptr->class);
  542.                hostargc = myclassptr->debugargc;
  543.                hostargv = myclassptr->debugargv;
  544.                Dprintf("debug argc = %d\n", hostargc);
  545.             }
  546.  
  547.             if (hostargv != NULL) {
  548.                if (hostargc == 0) {
  549.                   classargc--;
  550.                   for (loop2 = loop; loop2 < MAXEXECARGS-1; loop2++)
  551.                      classargv[loop2] = classargv[loop2 + 1];
  552.                } else {
  553.                   classargc += hostargc - 1;
  554.                   for (loop2 = MAXEXECARGS - hostargc; loop2 > loop; loop2--)
  555.                      classargv[loop2 + hostargc - 1] = classargv[loop2];
  556.                   for (loop2 = loop; loop2 < loop + hostargc; loop2++)
  557.                      classargv[loop2] = hostargv[loop2 - loop];
  558.                   loop += hostargc - 1;
  559.                }
  560.             }
  561.          }
  562.  
  563.          Dprintf("(after host flag insertion) classargc = %d\n",
  564.             classargc);
  565.  
  566.          for (loop = 0; loop <= classargc; loop++) {
  567.             /* WARNING: we are mangling the xargv array here! */
  568.             processarg(loop, hostptr, classptr);
  569.  
  570.             if (classargv[loop] == NULL) {
  571.                Dprintf("arg[%d] = NULL\n", loop);
  572.             } else {
  573.                Dprintf("arg[%d] = %s\n", loop, classargv[loop]);
  574.             }
  575.          }
  576.  
  577.          (void) execvp(classptr->xpath, classargv);
  578.  
  579.          logerr("%s: can't exec %s\n", hostptr->hostname, classptr->xpath);
  580.  
  581.          (void) _exit(1); /* could not exec the news transmitter! */
  582.       } else {
  583.      /* Parent. */
  584.      if (pid == -1) {
  585.         logerr("host %s: Can't fork child.\n", hostptr->hostname);
  586.         continue;
  587.      }
  588.          for (loop = 0; loop < MAXXMITTERS; loop++) {
  589.             if (pidlist[loop] == 0) {
  590.                pidlist[loop] = pid;
  591.                pidmap[loop] = hostptr;
  592.                Dprintf("%s: entered into pidmap/list at %d\n",
  593.                   hostptr->hostname, loop);
  594.                break;
  595.             }
  596.          }
  597.          hostptr->pid = pid;
  598.          hostptr->lasttime = clock;
  599.          classptr->laststart = clock;
  600.          classptr->curxmits++;
  601.          if (newsxdebug) xmit_done(-1); /* wait for xmitter to complete */
  602.       }
  603.    }
  604.  
  605.    if (classptr->members == hadtheirchance) {
  606.       dprintf("class %s: add 1 to sernum (members %d, hadchance %d)\n",
  607.          classptr->classname, classptr->members, hadtheirchance);
  608.       classptr->xmitsernum++;
  609.    }
  610. }
  611.